package cn.com.xy.sms.sdk.service.number;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONObject;

import android.util.LruCache;
import cn.com.xy.sms.sdk.Iservice.OnlineUpdateCycleConfigInterface;
import cn.com.xy.sms.sdk.constant.Constant;
import cn.com.xy.sms.sdk.db.entity.PhoneNumServe;
import cn.com.xy.sms.sdk.db.entity.PhoneNumServeManager;
import cn.com.xy.sms.sdk.db.entity.pubinfo.PubInfoManager;
import cn.com.xy.sms.sdk.dex.DexUtil;
import cn.com.xy.sms.sdk.iccid.IccidLocationUtil;
import cn.com.xy.sms.sdk.log.LogManager;
import cn.com.xy.sms.sdk.net.NetUtil;
import cn.com.xy.sms.sdk.net.util.ServerUtil;
import cn.com.xy.sms.sdk.threadpool.SmartSmsThreadPoolManager;
import cn.com.xy.sms.sdk.util.CallBackState;
import cn.com.xy.sms.sdk.util.StringUtils;
import cn.com.xy.sms.sdk.util.XyUtil;
import cn.com.xy.sms.util.SdkCallBack;

/* SDK-681 zhaojiangwei 20161121 start */
public class PhoneNumServeService {
    public final static int MENU_CALLBACK_ERROR = 100;
    public final static int MENU_CALLBACK_EXCEPTION = 101;
    public final static int MENU_CALLBACK_DATA = 102;

    private static final LruCache<String, JSONObject> PHONENUM_MENU_CACHE = new LruCache<String, JSONObject>(100);

    /**
     * 网络查询(多个)企业号码对应的菜单服务数据(最大支持50个)
     * 
     * @param phoneNums 号码列表
     * @param extend 扩展参数(可为空)，支持的字段如下
     * 				iccid: sim卡对应的iccid
     * 				code： 区位码，如北京("BJ"), 广东("GD")
     * 				simIndex:卡位， -1代表默认， 0代表卡1， 1代表卡2
     * 				cnum：短信中心号码
     * @param callBack 
     *            index0: 
     *                  PhoneNumServeService.MENU_CALLBACK_DATA:数据正常返回
     *                  PhoneNumServeService.MENU_CALLBACK_EXCEPTION:查询出现异常
     *                  PhoneNumServeService.MENU_CALLBACK_ERROR:查询出错.
     *            index1:JSONArray
     *                  号码对应的菜单json数组.
     * @return
     */
    public static void baseQueryPhoneNumsServe(List<String> netQueryNums, final Map<String, String> extend, final SdkCallBack xyCallBack){
        if (netQueryNums == null || netQueryNums.size() == 0 || netQueryNums.size() > 50) {
        	XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_EXCEPTION, "exception params");
            return;
        }
        List<String> queryNums = new ArrayList<String>();
        for (int index = 0; index < netQueryNums.size(); index++) {
            String itemNum = StringUtils.getPhoneNumberNo86(netQueryNums.get(index));
            if (!queryNums.contains(itemNum)) {
                queryNums.add(itemNum);
            }
        }
        if (queryNums.size()==0) {
			return;
		}
        String cnum = StringUtils.getValueByKey(extend, "cnum");
        String acode = StringUtils.getValueByKey(extend, "code");
        String iccid = StringUtils.getValueByKey(extend, "iccid");
        int simIndex = PubInfoManager.parseSimIndex(StringUtils.getValueByKey(extend, "simIndex"));
        if (StringUtils.isNull(acode)) {
            acode = IccidLocationUtil.getAreaCodeByCnumOrIccid(cnum, simIndex, iccid, queryNums.get(0));
        }
        final String areaCode = acode;
        final List<String> netQueryPhones = queryNums;
        SmartSmsThreadPoolManager.pubInfoPool.execute(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				SdkCallBack netSdkCallBack = new SdkCallBack() {
		            @Override
		            public void execute(Object... obj) {
		                try {
		                	String result = null;
		                    if (obj != null && obj.length > 1) {
		                        int callBackState = (Integer) obj[0];
		                        if (callBackState == CallBackState.REQUEST_SUCCESS) {
		                        	result  = (String) obj[1];
		                            LogManager.e("phonemenu", "get result from net:" + result);
		                        }
		                    }
		                    if (!StringUtils.isNull(result)) {
		                        XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_DATA, result);
		                    }
		                    else{
		                    	XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_ERROR, "net error");
		                    }
		                }
		                catch (Throwable e) {
		                    // TODO: handle exception
		                	XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_ERROR, "exception error");
		                }
		            }
		        };
		        String requestContent = ServerUtil.getPhoneNumsMenuReq(netQueryPhones, areaCode);
		        NetUtil.executeServiceHttpRequest(NetUtil.GET_PHONE_MENU, requestContent, null,
		                netSdkCallBack);
			}
        });
    }
    
    /**
     * 查询(单个)企业号码对应的菜单服务数据
     * 
     * @param phoneNum 号码
     * @param extend 扩展参数(可为空)，支持的字段如下
     * 				iccid: sim卡对应的iccid
     * 				code： 区位码，如北京("BJ"), 广东("GD")
     * 				simIndex:卡位， -1代表默认， 0代表卡1， 1代表卡2
     * 				cnum：短信中心号码
     * @param callBack 
     *            index0: 
     *                  PhoneNumServeService.MENU_CALLBACK_DATA:数据正常返回
     *                  PhoneNumServeService.MENU_CALLBACK_EXCEPTION:查询出现异常
     *                  PhoneNumServeService.MENU_CALLBACK_ERROR:查询出错.
     *            index1:JSONObject
     *                  该号码对应的菜单json数据.
     * @return
     */
    public static void queryPhoneNumMenus(String phoneNum, final Map<String, String> extend,
            final SdkCallBack xyCallBack) {
        if (StringUtils.isNull(phoneNum)) {
            return;
        }
        final String phone = StringUtils.getPhoneNumberNo86(phoneNum);
        String cnum = StringUtils.getValueByKey(extend, "cnum");
        String acode = StringUtils.getValueByKey(extend, "code");
        String iccid = StringUtils.getValueByKey(extend, "iccid");
        int simIndex = PubInfoManager.parseSimIndex(StringUtils.getValueByKey(extend, "simIndex"));
        if (StringUtils.isNull(acode)) {
            acode = IccidLocationUtil.getAreaCodeByCnumOrIccid(cnum, simIndex, iccid, phoneNum);
        }
        checkUpdateBackGround();
        // 先从缓存里面查找
        final String key = String.format(PhoneNumServeManager.QUERY_KEY_FMT, phone, acode);
        LogManager.e("phonemen", "key:" + key);
        JSONObject resultObj = PHONENUM_MENU_CACHE.get(key);
        if (resultObj != null && resultObj.toString().length() > 2) {
        	LogManager.e("phonemenu", "get result from cache:" + resultObj.toString());
            XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_DATA, resultObj);
            return;
        }
        final String areaCode = acode;
        SmartSmsThreadPoolManager.pubInfoPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    // 从数据库里面查找.
                    JSONObject resultObj = null;
                    PhoneNumServe opResponse = PhoneNumServeManager.queryMenuResult(phone, areaCode);
                    if (opResponse != null && opResponse.jsResult.length() > 2) {
                        resultObj = new JSONObject(opResponse.jsResult);
                        PHONENUM_MENU_CACHE.put(key, resultObj);
                        LogManager.e("phonemenu", "get result from db:" + resultObj.toString());
                        XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_DATA, resultObj);
                        return;
                    }
                    //请求网络查询.
                    SdkCallBack netSdkCallBack = new SdkCallBack() {
                        @Override
                        public void execute(Object... obj) {
                            try {
                                JSONObject retResult = null;
                                if (obj != null && obj.length > 1) {
                                    int callBackState = (Integer) obj[0];
                                    if (callBackState == CallBackState.REQUEST_SUCCESS) {
                                        String result = (String) obj[1];

                                        JSONArray menuResults = new JSONArray(result);
                                        if (menuResults != null && menuResults.length() > 0) {
                                            for (int index = 0; index < menuResults.length(); index++) {
                                                JSONObject itemObj = (JSONObject) menuResults.get(index);
                                                String retNum = itemObj.getString("phone");
                                                if (retNum.equalsIgnoreCase(phone)) {
                                                    retResult = itemObj;
                                                }
                                                PHONENUM_MENU_CACHE.put(key, itemObj);
                                                PhoneNumServeManager.insertOrUpdate(retNum, areaCode, itemObj,
                                                        System.currentTimeMillis());
                                            }
                                        }
                                        LogManager.e("phonemenu", "get result from net:" + result);
                                    }
                                }
                                if (retResult != null) {
                                    XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_DATA, retResult);
                                }
                                else {
                                    XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_ERROR, null);
                                }
                            }
                            catch (Throwable e) {
                                LogManager.e(Constant.TAG, "PhoneNumMenuService net execute error:", e);
                                XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_EXCEPTION, null);
                            }
                        }
                    };
                    String requestContent = ServerUtil.getPhoneNumMenuReq(phone, areaCode);
                    NetUtil.executeServiceHttpRequest(NetUtil.GET_PHONE_MENU, requestContent, null, netSdkCallBack);
                }
                catch (Throwable e) {
                    LogManager.e(Constant.TAG, "PhoneNumMenuService thread run error:", e);
                    XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_EXCEPTION, null);
                }
            }
        });
    }

    public static Map<String, JSONObject> loadDBDataToCache(List<String> phoneNums, String areaCode){
		Map<String, JSONObject> dbObjects = null;
		try {
			dbObjects = PhoneNumServeManager.queryMenuResult(phoneNums, areaCode, PHONENUM_MENU_CACHE);
		}
		catch (Throwable e) {
			// TODO: handle exception
			LogManager.e(Constant.TAG, "loadDBDataToCache error:", e);
		}
		return dbObjects;
    }

    private static void callCachedPhoneMenus(List<JSONObject> cachedObjects, SdkCallBack xyCallBack) {
        if (cachedObjects != null && cachedObjects.size() > 0) {
            JSONArray retArray = new JSONArray();
            for (int index = 0; index < cachedObjects.size(); index++) {
                retArray.put(cachedObjects.get(index));
            }
            XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_DATA, retArray);
        }
    }
    
    /**
     * 查询(多个)企业号码对应的菜单服务数据(最大支持50个)
     * 
     * @param phoneNums 号码列表
     * @param extend 扩展参数(可为空)，支持的字段如下
     * 				iccid: sim卡对应的iccid
     * 				code： 区位码，如北京("BJ"), 广东("GD")
     * 				simIndex:卡位， -1代表默认， 0代表卡1， 1代表卡2
     * 				cnum：短信中心号码
     * @param callBack 
     *            index0: 
     *                  PhoneNumServeService.MENU_CALLBACK_DATA:数据正常返回
     *                  PhoneNumServeService.MENU_CALLBACK_EXCEPTION:查询出现异常
     *                  PhoneNumServeService.MENU_CALLBACK_ERROR:查询出错.
     *            index1:JSONArray
     *                  号码对应的菜单json数组.
     * @return
     */
    public static void queryPhoneNumsMenus(List<String> phoneNums, final Map<String, String> extend,
            final SdkCallBack xyCallBack) {
        if (phoneNums == null || phoneNums.size() == 0 || phoneNums.size() > 50) {
            return;
        }
        String cnum = StringUtils.getValueByKey(extend, "cnum");
        String acode = StringUtils.getValueByKey(extend, "code");
        String iccid = StringUtils.getValueByKey(extend, "iccid");
        int simIndex = PubInfoManager.parseSimIndex(StringUtils.getValueByKey(extend, "simIndex"));

        List<String> queryNums = new ArrayList<String>();
        List<String> tmpQueryNums = new ArrayList<String>();
        for (int index = 0; index < phoneNums.size(); index++) {
            String itemNum = StringUtils.getPhoneNumberNo86(phoneNums.get(index));
            if (!queryNums.contains(itemNum)) {
                queryNums.add(itemNum);
            }
        }
        // 备份需要查询的号码.
        tmpQueryNums.addAll(queryNums);
        List<JSONObject> cachedObjects = new ArrayList<JSONObject>();
        if (StringUtils.isNull(acode) && queryNums.size() > 0) {
            acode = IccidLocationUtil.getAreaCodeByCnumOrIccid(cnum, simIndex, iccid, queryNums.get(0));
        }
        //先从缓存里面查找, 查到的先缓存起来
        for (int index = 0; index < queryNums.size(); index++) {
            String key = String.format(PhoneNumServeManager.QUERY_KEY_FMT, queryNums.get(index), acode);
            JSONObject resultObj = PHONENUM_MENU_CACHE.get(key);
            if (resultObj != null && resultObj.toString().length() > 2) {
            	LogManager.e("phonemenu", "get result from cache,key:" + key + "result:" + resultObj.toString());
                cachedObjects.add(resultObj);

                tmpQueryNums.remove(queryNums.get(index));
            }
        }
        if (cachedObjects.size() > 0) {
            callCachedPhoneMenus(cachedObjects, xyCallBack);
            LogManager.e("phonemenu", "tmpQueryNums:"+tmpQueryNums.toString());
        }
        // 还有缓存里面没有数据的号码.
        if (tmpQueryNums.size() > 0) {
            final String areaCode = acode;
            final List<String> dbQueryNums = tmpQueryNums;
            SmartSmsThreadPoolManager.mUploadPubInfoPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 备份需要查询的号码.
                        List<String> netQueryNums = new ArrayList<String>();
                        netQueryNums.addAll(dbQueryNums);

                        // 从数据库里面查找.
                        List<JSONObject> cachedDdObjects = new ArrayList<JSONObject>();
                        JSONObject resultObj = null;
                        
                        Map<String, JSONObject> phoneNumMenus = PhoneNumServeManager.queryMenuResult(dbQueryNums, areaCode, PHONENUM_MENU_CACHE);
                        if (phoneNumMenus!=null) {
            				Iterator<Map.Entry<String, JSONObject>> entries = phoneNumMenus.entrySet().iterator();  
            				while (entries.hasNext()) {  
            				    Map.Entry<String, JSONObject> entry = entries.next();  
            				    resultObj = entry.getValue();
            				    
            				    cachedDdObjects.add(resultObj);
            				    netQueryNums.remove(resultObj.optString("phone"));
            				} 
						}
                        
                        if (cachedDdObjects.size() > 0) {
                            callCachedPhoneMenus(cachedDdObjects, xyCallBack);
                            LogManager.e("phonemenu", "netQueryNums:"+netQueryNums.toString());
                        }
                        //还需要网络查询.
                        if (netQueryNums.size() > 0) {
                        	queryPhoneNumsFromAPI(netQueryNums, areaCode, xyCallBack);
                        }
                    }
                    catch (Exception e) {
                        // TODO: handle exception
                    	XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_ERROR, "exception error");
                    }
                }
            });
        }
        checkUpdateBackGround();
    }
    
    private static void queryPhoneNumsFromAPI(List<String> netQueryNums, final String areaCode, final SdkCallBack xyCallBack){
    	SdkCallBack netSdkCallBack = new SdkCallBack() {
            @Override
            public void execute(Object... obj) {
                try {
                    JSONArray menuResults = null;
                    if (obj != null && obj.length > 1) {
                        int callBackState = (Integer) obj[0];
                        if (callBackState == CallBackState.REQUEST_SUCCESS) {
                            String result = (String) obj[1];
                            menuResults = new JSONArray(result);
                            if (menuResults != null && menuResults.length() > 0) {
                                for (int index = 0; index < menuResults.length(); index++) {
                                    JSONObject itemObj = (JSONObject) menuResults.get(index);
                                    String retPhoneNum = itemObj.getString("phone");
                                    String key = String.format(PhoneNumServeManager.QUERY_KEY_FMT, retPhoneNum, areaCode);
                                    PHONENUM_MENU_CACHE.put(key, itemObj);
                                    PhoneNumServeManager.insertOrUpdate(retPhoneNum, areaCode, itemObj, System.currentTimeMillis());
                                }
                            }
                            LogManager.e("phonemenu", "get result from net:" + result);
                        }
                    }
                    if (menuResults != null && menuResults.length() > 0) {
                        XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_DATA, menuResults);
                    }
                    else{
                    	XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_ERROR, "null error");
                    }
                }
                catch (Throwable e) {
                    // TODO: handle exception
                	XyUtil.doXycallBackResult(xyCallBack, MENU_CALLBACK_ERROR, "exception error");
                }
            }
        };
        String requestContent = ServerUtil.getPhoneNumsMenuReq(netQueryNums, areaCode);
        NetUtil.executeServiceHttpRequest(NetUtil.GET_PHONE_MENU, requestContent, null,
                netSdkCallBack);
    }
    
    private static long mLastCheckTime = 0;

	private static void checkUpdateBackGround() {
		if (mLastCheckTime + Constant.FIVE_MINUTES < System.currentTimeMillis()) {
			mLastCheckTime = System.currentTimeMillis();
			SmartSmsThreadPoolManager.mUploadPubInfoPool.execute(new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					long updateCycle =DexUtil.getUpdateCycleByType(OnlineUpdateCycleConfigInterface.TYPE_PHONE_SERVICE_CYCLE,
							 Constant.DAY);
					LogManager.e("phonemenu", "updateCycle1:"+updateCycle);
					long offsetTime = System.currentTimeMillis() - updateCycle;
					checkUpdate(offsetTime, 1);
					
					updateCycle =DexUtil.getUpdateCycleByType(OnlineUpdateCycleConfigInterface.TYPE_PHONE_SERVICE_CYCLE_NODATA,
							 Constant.NETQUERYPRIO);
					LogManager.e("phonemenu", "updateCycle2:"+updateCycle);
					offsetTime = System.currentTimeMillis() - updateCycle;
					checkUpdate(offsetTime, -1);
				}
			});
		}
		else {
			LogManager.e("phonemenu", "no need update menus.");
		}
	}
	
	private static void checkUpdate(long offsetTime, int status){
		try {
			List<PhoneNumServe> updateNums = PhoneNumServeManager.queryPhoneAndAreaCodes(offsetTime, status);
			
			List<String>phones = new ArrayList<String>();
			for(int index=0; index<updateNums.size(); index++){
				phones.add(updateNums.get(index).phoneNum);
			}
			LogManager.e("phonemenu", "backgroud update status:" +status+", phones:"+phones.toString());
			int totalCount = updateNums.size();
			if (totalCount > 0) {
				int MENU_UPDATE_LIMIT = 20;
				int MAX_CHECH_COUNT = 10;
				double updateCount = Math.ceil(Double.parseDouble(String.valueOf(totalCount)) / MENU_UPDATE_LIMIT);
				if (updateCount > MAX_CHECH_COUNT) {
					updateCount = MAX_CHECH_COUNT;
					totalCount = (int) (updateCount) * MENU_UPDATE_LIMIT;
				}
				for (int i = 0; i < updateCount; i++) {
					int start = (i * MENU_UPDATE_LIMIT);
					int end = (((i + 1) == updateCount) ? totalCount : ((i + 1) * MENU_UPDATE_LIMIT));
					backGroundUpdate(updateNums.subList(start, end));
				}
			}
		}
		catch (Throwable e) {
			// TODO: handle exception
			LogManager.e(Constant.TAG, "checkUpdate exception", e);
		}
	}
	
	
	private static void backGroundUpdate(final List<PhoneNumServe>phoneMenus){
		if (phoneMenus!=null && phoneMenus.size()>0) {
			SdkCallBack netSdkCallBack = new SdkCallBack() {
	            @Override
	            public void execute(Object... obj) {
	                try {
	                    JSONArray menuResults = null;
	                    if (obj != null && obj.length > 1) {
	                        int callBackState = (Integer) obj[0];
	                        if (callBackState == CallBackState.REQUEST_SUCCESS) {
	                            String result = (String) obj[1];
	                            menuResults = new JSONArray(result);
	                            if (menuResults != null && menuResults.length() > 0) {
	                                for (int index = 0; index < menuResults.length(); index++) {
	                                    JSONObject itemObj = (JSONObject) menuResults.get(index);
	                                    String retPhoneNum = itemObj.getString("phone");
	                                    String retCode = itemObj.getString("area");
	                                    String key = String.format(PhoneNumServeManager.QUERY_KEY_FMT, retPhoneNum, retCode);
	                                    PHONENUM_MENU_CACHE.put(key, itemObj);
	                                    PhoneNumServeManager.insertOrUpdate(retPhoneNum, retCode, itemObj, System.currentTimeMillis());
	                                }
	                            }
	                            LogManager.e("phonemenu", "get result from net:" + result);
	                            //更新查询时间.
	                            PhoneNumServeManager.updateLastCheckTime(phoneMenus, System.currentTimeMillis());
	                        }
	                    }
	                }
	                catch (Throwable e) {
	                    // TODO: handle exception
	                	LogManager.e(Constant.TAG, "backGroundUpdate exception", e);
	                }
	            }
	        };
			String requestContent = ServerUtil.getPhoneNumsMenuReq(phoneMenus);
	        NetUtil.executeServiceHttpRequest(NetUtil.GET_PHONE_MENU, requestContent, null,
	                 netSdkCallBack);
		}
	}
}

/* SDK-681 zhaojiangwei 20161121 end */
